package org.ws.edu.task1.qizer;

import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.ws.edu.task1.IConsoleHandler;

/**
 * Created by IntelliJ IDEA.
 * User: qizer
 * Date: 29.09.11
 * Time: 14:05
 * It's work!
 */

public class ConsoleHandlerQ implements IConsoleHandler {
    int i;
    private static Pattern PAT_DIGIT_ALL = Pattern.compile("([(-9^!]+)");
    private static Pattern PAT_DIGIT = Pattern.compile("([0-9.])+");
    private static Pattern PAT_ZN_ALL = Pattern.compile("([-*+/^!])");
    private static Pattern PAT_ZN = Pattern.compile("([-*+/^])");
    CalculatorQ calc = new CalculatorQ();

    @Override
    public String handle(String line) {
        if (line.equals("exit"))
            return "exit";
        line = correctString(line);
        String tmpStr = line;
        try {
            tmpStr = validateString(tmpStr);
        } catch (IncorrectEntryLenght incorrectEntryLenght) {
            return tmpStr = "Small Lenght";
        } catch (IncorrectHooks incorrectHooks) {
            return tmpStr = "Incorrect hooks";
        } catch (IncorrectInputSumbols incorrectInputSumbols) {
            return tmpStr = "Incorrect input symbols";
        } catch (IllegalArgumentException error) {
            return tmpStr = "Incorrect input symbols1";
        } catch (Exception e) {
            e.printStackTrace(); // To change body of catch statement use File | Settings | File Templates.
        }

        String rez = calculation(convertToOPZ(tmpStr));
        // tmpStr = tmpStr + "= " + rez;
        tmpStr = "" + rez;
        return tmpStr;
    }

    /**
     * Корректировка строки, удаление пробелов и замена запятых на точки, дабы дробные числа были
     */
    private String correctString(String tmpStr) {
        tmpStr = tmpStr.replace(" ", "");
        tmpStr = tmpStr.replace(",", ".");
        return tmpStr;
    }

    /**
     * Проверка строки на различные ошибки воода и информирование пользователей
     */
    private String validateString(String tmpStr) throws Exception, IncorrectEntryLenght, IncorrectHooks,
            IncorrectInputSumbols {
        /**
         * Проверка на количество символов
         */
        {
            if (tmpStr.length() < 2)
                throw new IncorrectEntryLenght();
        }

        /**
         * Проверяем скобки
         */
        {
            int open = 0, close = 0;
            for (i = 0; i < tmpStr.length(); i++) {
                switch (tmpStr.charAt(i)) {
                    case '(':
                        open++;
                        break;
                    case ')':
                        close++;
                        break;
                }
                if (close > open)
                    break;
            }
            if (open != close)
                throw new IncorrectHooks();
        }
        /**
         * Проверка на допустимые символы
         */
        {
            Matcher MATCH_PAT_DIGIT_ALL = PAT_DIGIT_ALL.matcher(tmpStr);
            if (MATCH_PAT_DIGIT_ALL.matches() != true)
                throw new IllegalArgumentException();
            // throw new IncorrectInputSumbols();
        }
        return tmpStr;
    }

    /**
     * Переводим в обратную польскую запись
     */
    private Stack convertToOPZ(String tmpStr) {
        Stack stack = new Stack();
        Stack outStack = new Stack();
        for (i = 0; i < tmpStr.length(); i++) {
            char c = tmpStr.charAt(i);
            Matcher MATCH_PAT_ZN_ALL = PAT_ZN_ALL.matcher(String.valueOf(c));
            if (Character.isDigit(c) || c == '.') {
                String tmpS = String.valueOf(c);
                i++;
                for (int j = i; j < tmpStr.length(); j++) {
                    c = tmpStr.charAt(j);
                    if (Character.isDigit(c) || c == '.') {
                        tmpS += c;
                        i++;
                    } else {
                        i--;
                        break;
                    }
                }
                outStack.push(tmpS);
                continue;
            }
            if (c == '(') {
                stack.push(c);
                continue;
            }
            if (c == ')') {
                while (String.valueOf(stack.peek()).charAt(0) != '(') {
                    outStack.push(stack.pop());
                }
                stack.pop();
                continue;
            }
            if (MATCH_PAT_ZN_ALL.matches() == true) {
                while ((stack.empty() != true) && (prior(c) <= prior(String.valueOf(stack.peek()).charAt(0)))) {
                    outStack.push(stack.pop());
                }
                stack.push(c);
            }
        }
        while (stack.empty() != true) {
            outStack.push(stack.pop());
        }
        return outStack;
    }

    /**
     * Игра приоритетов
     */
    private int prior(char a) {
        switch (a) {
            case '!':
                return 5;
            case '^':
                return 4;
            case '*':
            case '/':
                return 3;
            case '-':
            case '+':
                return 2;
            case '(':
                return 1;
        }
        return 0;
    }

    /**
     * Само вычисление
     */
    private String calculation(Stack mStack) {
        double rez = 0, a = 0, b = 0;
        Stack stack = new Stack();
        while (mStack.size() != 0) {
            stack.push(mStack.pop());
        }
        while (stack.size() != 0) {
            String str = String.valueOf(stack.pop());
            Matcher MATCH_PAT_ZN = PAT_ZN.matcher(str);
            Matcher MATCH_PAT_DIGIT = PAT_DIGIT.matcher(str);
            if (MATCH_PAT_DIGIT.matches() == true) {
                mStack.push(str);
                continue;
            }
            if (str.equals("!")) {
                rez = calc.factorialD(Double.valueOf(String.valueOf(mStack.pop())));
                mStack.push(rez);
                continue;
            }
            if (MATCH_PAT_ZN.matches() == true) {
                b = Double.valueOf(String.valueOf(mStack.pop()));
                a = Double.valueOf(String.valueOf(mStack.pop()));
                rez = calc.calculat(a, b, str.charAt(0));
                mStack.push(rez);
                continue;
            }
        }
        String rezult = String.valueOf(rez);
        return rezult;
    }

    /**
     * Наши ошибочки
     */
    private class IncorrectHooks extends Throwable {
        private IncorrectHooks() {
            super();
        }
    }

    private class IncorrectEntryLenght extends Throwable {
        private IncorrectEntryLenght() {
            super();
        }
    }

    private class IncorrectInputSumbols extends Throwable {
        private IncorrectInputSumbols() {
            super();
        }
    }
}
